<?php


namespace App\Service\OpenShop\Feishu;

use App\Enums\OpenShop\Feishu\CacheEnum;
use App\Enums\OpenShop\InvestmentPromotion\ErrCode;
use App\Exceptions\Custom\CustomHttpResponseException;
use App\Http\Middleware\OpenShop\UserPermissionAuth;
use App\Models\Common\ConfigModel;
use App\Models\Common\FsCompanyUserMappingModel;
use App\Models\OpenShop\InvestmentPromotion\OpenShopSiteMissionModel;
use App\Models\OpenShop\SiteLocation\OpenShopSiteLocationModel;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

class BaseService
{


    //获取企业应用token
    const TENANT_ACCESS_TOKEN_URL = 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal';
    //获取Ticket
    const GET_TICKET_URL = 'https://open.feishu.cn/open-apis/jssdk/ticket/get';

    const SEND_MESSAGE_URL = 'https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type';

    const BATCH_SEND_MESSAGE_URL = 'https://open.feishu.cn/open-apis/message/v4/batch_send';

    const GET_USER_INFO_URL = 'https://open.feishu.cn/open-apis/authen/v1/access_token';

    const GET_APP_ACCESS_TOKEN_URL = "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal";

    /**
     * 获取token
     */
    public static function getTenantAccToken(string $module = UserPermissionAuth::MODULE_OPEN_SHOP, int $orgId = 0, string $receivedId = '')
    {
        try {
            $config = ConfigModel::getFsConfigByPlatformNameOrgId($module, $orgId);
            if (!isset($config['appId'])) {
                Log::warning("getTenantAccToken und index: {$module} {$orgId} {$receivedId}");

                // 记录一下调用栈的，方便排查
                $backtrace = debug_backtrace();
                Log::warning('getTenantAccToken backtrace', [
                    '1' => [
                        $backtrace[1]['class'], $backtrace[1]['function'], $backtrace[1]['args']
                    ],
                    '2' => [
                        $backtrace[2]['class'], $backtrace[2]['function'], $backtrace[2]['args']
                    ],
                    '3' => [
                        $backtrace[3]['class'], $backtrace[3]['function'], $backtrace[3]['args']
                    ],
                ]);
            }

            $appid     = $config['appId'];
            $appSecret = $config['appSecret'];

            $tenantToken = Redis::get(CacheEnum::PREFIX_T_ACCESS_TOKEN . ":" . $appid);
            if (!$tenantToken) {
                $client      = new GuzzleClient();
                $body        = ['app_id' => $appid, 'app_secret' => $appSecret];
                $body        = json_encode($body);
                $url         = self::TENANT_ACCESS_TOKEN_URL;
                $response    = $client->request("POST", $url, ['body' => $body, 'timeout' => 10]);
                $response    = $response->getBody()->getContents();
                $responseArr = json_decode($response, true);

                Redis::setex(
                    CacheEnum::PREFIX_T_ACCESS_TOKEN . ":" . $appid, $responseArr['expire'],
                    $responseArr['tenant_access_token']
                );
                return $responseArr['tenant_access_token'];
            } else {
                return $tenantToken;
            }
        } catch (\Exception $exception) {
            throw new CustomHttpResponseException("获取TenantAccToken失败", $exception);
        }
    }

    public static function getTicket(string $module = UserPermissionAuth::MODULE_OPEN_SHOP)
    {
        try {
            $appid  = config("feishu.{$module}.appID");
            $ticket = Redis::get(CacheEnum::JSAPI_TICKET . $appid);
            if (!$ticket) {
                $accToken   = 'Bearer ' . self::getTenantAccToken();
                $url        = self::GET_TICKET_URL;
                $client     = new GuzzleClient();
                $response   = $client->request('POST', $url, [
                    'headers' => [
                        'Content-Type'  => "application/json; charset=utf-8",
                        'Authorization' => $accToken
                    ]
                ]);
                $response   = $response->getBody()->getContents();
                $jsonDecode = json_decode($response, true);
                Redis::setex(
                    CacheEnum::JSAPI_TICKET . ":" . $appid, $jsonDecode['data']['expire_in'],
                    $jsonDecode['data']['ticket']
                );
                return $jsonDecode['data']['ticket'];
            }
            return $ticket;
        } catch (\Exception $exception) {
            throw new CustomHttpResponseException("获取Ticket失败", $exception);
        }
    }

    /**
     * 发送消息
     * @param string $receiveId 依据receive_id_type的值，填写对应的消息接收者id
     * @param string $receiveType open_id/user_id/union_id/email/chat_id
     * @param array $data 消息内容
     * @param string $messageType 消息类型 包括：text、post、image、file、audio、media、sticker、interactive、share_chat、share_user
     * @throws GuzzleException
     */
    public static function sendMessage(
        string $receiveId,
        string $receiveType,
        array $data,
        string $messageType,
        string $module
    ) {
        if (!$receiveId) {
            return [];
        }

        $orgId = FsCompanyUserMappingModel::getOrganizationIdByFeiShuId($receiveId);

        try {
            $accToken = 'Bearer ' . self::getTenantAccToken($module, $orgId, $receiveId);
            $url      = self::SEND_MESSAGE_URL . '=' . $receiveType;
            $client   = new GuzzleClient();
            $body     = [
                'receive_id' => $receiveId,
                'content'    => json_encode($data),
                'msg_type'   => $messageType
            ];
            $body     = json_encode($body);
            $response = $client->request('POST', $url, [
                'body'        => $body,
                'headers'     => [
                    'Content-Type'  => "application/json; charset=utf-8",
                    'Authorization' => $accToken
                ],
                'timeout'     => 10,
                'http_errors' => false,
            ]);
            $response = $response->getBody()->getContents();
            return json_decode($response, true);
        } catch (\Exception $exception) {
            throw new CustomHttpResponseException(
                "{$receiveId}-飞书消息发送失败:" . json_encode(
                    $data,
                    JSON_UNESCAPED_UNICODE
                ), $exception
            );
        }
    }

    /**
     * 批量发 - 以后别用了module不能传外部的!!!!!!!!!!!!!!!
     *
     * @param array $data
     * @param string $module
     * @param array $departmentIds
     * @param array $openIds
     * @param array $userIds
     * @param array $unionIds
     * @return mixed
     * @throws GuzzleException
     */
    public static function batchSendCardMessage(
        array $data,
        string $module,
        array $userIds = [],
        array $departmentIds = [],
        array $openIds = [],
        array $unionIds = [],
        int $orgId = 0
    ) {
        try {
            $accToken = 'Bearer ' . self::getTenantAccToken($module, $orgId);
            $url      = self::BATCH_SEND_MESSAGE_URL;
            $client   = new GuzzleClient();
            $userIds  = array_values(array_filter($userIds));
            $body     = [
                'card'           => $data,
                'msg_type'       => 'interactive',
                'department_ids' => $departmentIds,
                'open_ids'       => $openIds,
                'user_ids'       => $userIds,
                'union_ids'      => $unionIds,
            ];
            $body     = array_filter($body);
            $body     = json_encode($body);
            $response = $client->request('POST', $url, [
                'body'        => $body,
                'headers'     => [
                    'Content-Type'  => "application/json; charset=utf-8",
                    'Authorization' => $accToken
                ],
                'timeout'     => 10,
                'http_errors' => false,
            ]);
            $response = $response->getBody()->getContents();
            return json_decode($response, true);
        } catch (\Exception $exception) {
            throw new CustomHttpResponseException(
                "飞书消息发送失败:" . json_encode(
                    $data,
                    JSON_UNESCAPED_UNICODE
                ), $exception
            );
        }
    }

    /**
     * 上传文件
     *
     * @param string $module
     * @param $file
     * @return mixed
     * @throws GuzzleException
     */
    public static function uploadFeishuImage(string $module, $file, int $orgId = 0)
    {
        try {
            $accToken = 'Bearer ' . self::getTenantAccToken($module, $orgId);
            $client   = new GuzzleClient();
            $response = $client->request('POST', "https://open.feishu.cn/open-apis/im/v1/images", [
                'multipart'   => [
                    [
                        'name'     => 'image_type',
                        'contents' => 'message',
                    ],
                    [
                        'name'     => 'image',
                        'contents' => fopen($file, 'r'),
                    ]
                ],
                'headers'     => [
                    //'Content-Type'  => "application/x-www-form-urlencoded",
                    'Authorization' => $accToken
                ],
                'timeout'     => 10,
                'http_errors' => false,
            ]);
            $response = json_decode($response->getBody()->getContents(), true);
            if ($response['code'] == 0) {
                return $response['data']['image_key'] ?? '';
            }
        } catch (\Exception $exception) {
            throw new CustomHttpResponseException(
                "图片上传失败:" . json_encode(
                    [],
                    JSON_UNESCAPED_UNICODE
                ), $exception
            );
        }
    }

    public static function getUserInfo($code, string $webPlatform, string $appid)
    {
        try {
            $accToken = 'Bearer ' . self::getAppAccessToken($webPlatform, $appid);
            $client   = new GuzzleClient();
            $body     = ['grant_type' => 'authorization_code', 'code' => $code];
            $body     = json_encode($body);
            $url      = self::GET_USER_INFO_URL;
            $response = $client->request("POST", $url, [
                'body'    => $body,
                'headers' => [
                    'Content-Type'  => "application/json; charset=utf-8",
                    'Authorization' => $accToken
                ],
                'timeout' => 10
            ]);
            $response = $response->getBody()->getContents();
            $decode   = json_decode($response, true);

            if ($decode['code'] == '20007') {
                \App\Service\BaseService::throwErr($decode['msg'] ?? '登录失败', $decode['code'] ?? 500);
            }

            if ($decode['code'] != 0) {
                Log::warning("getUserInfo error:", [
                    "code"        => $code,
                    "webPlatform" => $webPlatform,
                    'appId'       => $appid,
                    'accToken'   => $accToken,
                    'response'    => $response,
                ]);
                throw new CustomHttpResponseException($decode['code'], null, $decode['msg'], [], true, false);
            }

            Log::info('BaseService_getUserInfo', $decode);
            return $decode;
        } catch (\Exception $exception) {
            throw new CustomHttpResponseException("获取用户信息失败", $exception);
        }

    }

    /**
     * 获取企业token
     * @return mixed
     * @throws GuzzleException
     */
    public static function getAppAccessToken(string $webPlatform = 'openShop', string $appid = '')
    {
        try {
            if (!empty($appid)) {

                // 新版登录
                $fsConfig  = ConfigModel::getFsConfigByAppId($appid);
                $appid     = $fsConfig['appId'];
                $appSecret = $fsConfig['appSecret'];

            } else {

                // 老版登录
                $appid     = config("feishu.{$webPlatform}.appID");
                $appSecret = config("feishu.{$webPlatform}.AppSecret");
            }

            if (!$appid || !$appSecret) {
                throw new CustomHttpResponseException(ErrCode::errCodeTrans(ErrCode::CODE_ERR_WEB_PLATFORM));
            }

            $appToken = Redis::get(CacheEnum::PREFIX_APP_ACCESS_TOKEN . ":" . $appid);
            if (!$appToken) {
                $client      = new GuzzleClient();
                $body        = ['app_id' => $appid, 'app_secret' => $appSecret];
                $body        = json_encode($body);
                $url         = self::GET_APP_ACCESS_TOKEN_URL;
                $response    = $client->request("POST", $url, [
                    'body'    => $body,
                    'headers' => [
                        'Content-Type' => "application/json; charset=utf-8",
                    ],
                    'timeout' => 10
                ]);
                $response    = $response->getBody()->getContents();
                $responseArr = json_decode($response, true);
                Redis::setex(
                    CacheEnum::PREFIX_APP_ACCESS_TOKEN . ":" . $appid, $responseArr['expire'],
                    $responseArr['app_access_token']
                );
                return $responseArr['app_access_token'];
            } else {
                return $appToken;
            }
        } catch (\Exception $exception) {
            throw new CustomHttpResponseException("获取AppAccessToken失败", $exception);
        }

    }

    /**
     * 获取跳转飞书应用内链接
     * @param $orderNo
     * @return string
     */
    public static function getOrderInfoWenAppUrl($orderNo, $missionId = 0): string
    {
        $appid = config('feishu.openShop.appID');
        if ($missionId == 0) {
            $path = "/openShop/order/info/{$orderNo}&topMenuId=45";
        } else {
            $path = "/openShop/mission/info/$missionId&topMenuId=45";
        }

        return "https://applink.feishu.cn/client/web_app/open?appId={$appid}&path=$path";
    }

    /**
     * 获取选址的飞书小程序的跳转地址
     */
    public static function getSiteAppletUrl(string $orderNo)
    {
        $appid = ConfigModel::getFsConfigByPlatformNameOrgId('site', 0)['appId'] ?? '';

        $url = "https://applink.feishu.cn/client/mini_program/open?appId={$appid}&mode=window-semi&relaunch=true&path=pages/site-info/index?orderNo={$orderNo}";
        return $url;
    }

    /**
     * 获取开店飞书小程序的跳转地址
     * @param string $siteId
     * @return string
     */
    public static function getOpenAppletUrl(int $missionId): string
    {
        $appid = ConfigModel::getFsConfigByPlatformNameOrgId('openShop', 0)['appId'] ?? '';

        $siteMission = OpenShopSiteMissionModel::query()->find($missionId);
        if (!$siteMission) {
            return '';
        }

        //        $path = urlencode("pages/task-detail/task-detail?id={$missionId}&stepStatus={$siteMission->stepStatus}&orderNo={$siteMission->orderNo}");
        $path = urlencode("pages/task-detail/task-detail?id={$missionId}");

        return "https://applink.feishu.cn/client/mini_program/open?appId={$appid}&mode=window-semi&relaunch=true&path={$path}";
    }

    /**
     * 获取选址飞书小程序的跳转地址
     * @param int $missionId
     * @return string
     */
    public static function getSiteLocationAppletUrl(int $missionId, string $orderNo = ''): string
    {
        $appid = ConfigModel::getFsConfigByPlatformNameOrgId('site', 0)['appId'] ?? '';

        if (mb_strlen($orderNo)) {
            $path = urlencode("pages/site-info/index?orderNo={$orderNo}");
        } else {
            $siteMission = OpenShopSiteMissionModel::query()->find($missionId);
            if (!$siteMission) {
                return '';
            }
            $path = urlencode("pages/site-info/index?orderNo={$siteMission->orderNo}");
        }

        return "https://applink.feishu.cn/client/mini_program/open?appId={$appid}&mode=window-semi&relaunch=true&path={$path}";
    }

    /**
     * 获取网规详情跳转地址
     * @param $type
     * @param int $id
     * @return string
     */
    public static function getWangguiAppletUrl($type, int $id): string
    {
        $appid = ConfigModel::getFsConfigByPlatformNameOrgId('site', 0)['appId'] ?? '';

        $path = is_numeric($type) ? urlencode("pages/site-plan/site-point-detail?id={$id}&type={$type}") : urlencode("pages/point-detail/detail?id={$id}&type={$type}"); //不同类型的详情
        return "https://applink.feishu.cn/client/mini_program/open?appId={$appid}&mode=window-semi&relaunch=true&path={$path}";
    }

    /**
     * 获取意向址跟进记录详情跳转地址
     * @param int $id
     * @param $lat
     * @param $lng
     * @return string
     */
    public static function getIntendedSiteLocationUrl(int $id, $lat, $lng): string
    {
        $appid = ConfigModel::getFsConfigByPlatformNameOrgId('site', 0)['appId'] ?? '';

        $path = urlencode("pages/self-contained/index?id={$id}&lat={$lat}&lng={$lng}&shopId=-1");

        return "https://applink.feishu.cn/client/mini_program/open?appId={$appid}&mode=window-semi&relaunch=true&path={$path}";
    }

    /**
     * 获取选址飞书小程序房源的跳转地址
     * @param int $missionId
     * @return string
     */
    public static function getSiteLocationAppletUrlByHouse(int $siteId): string
    {
        $appid = ConfigModel::getFsConfigByPlatformNameOrgId('site', 0)['appId'] ?? '';

        $path = urlencode("pages/house-source/add-source?id={$siteId}");

        return "https://applink.feishu.cn/client/mini_program/open?appId={$appid}&mode=window-semi&relaunch=true&path={$path}";
    }

    /**
     * 获取房源评估详情url
     * @param int $missionId
     * @return string
     */
    public static function getHouseEvaluateDetailUrl(int $siteId): string
    {
        $appid = ConfigModel::getFsConfigByPlatformNameOrgId('site', 0)['appId'] ?? '';

        $path = urlencode("pages/house-source/add-source?id={$siteId}");

        return "https://applink.feishu.cn/client/mini_program/open?appId={$appid}&mode=window-semi&relaunch=true&path={$path}";
    }

    /**
     * 巡店任务详情
     * @param int $orderId
     * @return string
     */
    public static function getCruiseTaskDetailUrl(int $orderId): string
    {
        $appid = ConfigModel::getFsConfigByPlatformNameOrgId('cruise', 0)['appId'] ?? '';

        $path = urlencode("pages/task-detail/task-detail?taskId={$orderId}");

        return "https://applink.feishu.cn/client/mini_program/open?appId={$appid}&mode=window-semi&relaunch=true&path={$path}";
    }

    /**
     * 开店小程序，跳板url-old
     * @param int $missionId
     * @return string
     */
    public static function getOpenBoardUrl(int $missionId): string
    {
        return config('app.url') . "openShop/message/mission/info/{$missionId}";
    }

    /**
     * 开店小程序，跳板url v2
     * @param array $data
     * @return string
     */
    public static function getOpenBoardUrlV2(array $data): string
    {
        return config('app.url') . "openShop/message/boardUrl?" . http_build_query($data);
    }

    /**
     * 跳转rms web的url
     * @param string $path
     * @return string
     */
    public static function pcUrl(string $path): string
    {
        $appid = config('feishu.openShop.appID');

        return "https://applink.feishu.cn/client/web_app/open?appId={$appid}&path=$path";
    }

    /**
     * 获取评价反馈跳转飞书应用内链接
     * @param int $id
     * @return string
     */
    public static function getAppraiseInfoWenAppUrl(int $id): string
    {
        $appid = config("feishu." . UserPermissionAuth::MODULE_WORK_ORDER . ".appID");
        $path  = "/appraise/info/{$id}?isMainModule=1";
        return "https://applink.feishu.cn/client/web_app/open?appId={$appid}&path=$path";
    }


    /**
     * 获取跳转飞书应用内链接-运营中心
     * @param $orderNo
     * @return string
     */
    public static function getOrderInfoWebAppUrl($orderNo): string
    {
        $appid = config('feishu.center.appID');
        $path  = "/openShop/order/info/{$orderNo}&topMenuId=45";

        return "https://applink.feishu.cn/client/web_app/open?appId={$appid}&path=$path";
    }

    /**
     * 巡检报告详情
     * @param int $id
     * @return string
     */
    public static function getCruiseCheckDetailUrl(int $id): string
    {
        $appid = ConfigModel::getFsConfigByPlatformNameOrgId('cruiseCheck', 0)['appId'] ?? '';

        $path = urlencode("pages/report/report-detail?id={$id}");

        return "https://applink.feishu.cn/client/mini_program/open?appId={$appid}&mode=window-semi&relaunch=true&path={$path}";
    }

    /**
     * 跳转到开票记录列表，筛选项默认选中该门店/日期
     * @param int $id
     * @param string $startTime
     * @param string $endTime
     * @return string
     */
    public static function getInvoiceListUrl(int $id = 0, string $startTime = '', string $endTime = ''): string
    {
        $appid = config("feishu." . UserPermissionAuth::MODULE_WORK_ORDER . ".appID");
        if ($id) {// 跳转到详情
            $path = "/operation/finance/invoice/info/{$id}";
        } else {// 跳转到列表 时间范围查询
            $path = "/operation/finance/invoice/list?startTime={$startTime}&endTime={$endTime}";
        }
        return "https://applink.feishu.cn/client/web_app/open?appId={$appid}&path=$path";
    }

    /**
     * 工单详情
     * @param string $workOrderNo
     * @return string
     */
    public static function getWorkOrderDetailUrl(string $workOrderNo): string
    {
        $path  = "workOrder/manage/info/{$workOrderNo}?isMainModule=1";
        $appid = config('feishu.' . UserPermissionAuth::MODULE_WORK_ORDER . '.appID');

        return "https://applink.feishu.cn/client/web_app/open?appId={$appid}&path=$path";
    }

    /**
     * 通用的运营后台url
     * @param string $path
     * @return string
     */
    public static function commonWebUrl(string $path): string
    {
        $appid = config('feishu.center.appID');
//        $path  = "/openShop/order/info/{$orderNo}&topMenuId=45";

        return "https://applink.feishu.cn/client/web_app/open?appId={$appid}&path=$path";
    }
}
